// * 对象属性有两种：

let user = {
	// 1. [数据属性]
	name: 'Jake',

	// 2. 具有 getter 和 setter [访问器属性]
	get propName() {
		// 当读取 user.propName 时，getter 起作用（不以函数的形式调用，正常获取即可，getter 在后台起作用）
	},

	set propName(value) {
		// 当进行属性赋值时 user.propName = 'value' 时，setter 起作用（不以函数的形式调用，正常赋值即可，setter 在后台起作用）
	},
}

// ******

// 例子：
let obj = {
	name: 'John',
	surname: 'Smith',

	get fullName() {
		return `${this.name} ${this.surname}`
	},

	set fullName(value) {
		;[this.name, this.surname] = value.split(' ')
	},
}

obj.fullName = 'John Doe'

console.log(obj.name, obj.surname) // John Doe
console.log(obj.fullName) // John Doe

// **********************************************************************************************************************************

// * 访问器属性描述符：访问器属性的标志，没有 value 和 writable，但是有 get 和 set 函数。

// * 所以访问器属性可能有标识：
// * get 一个没有参数的函数，在读取属性时工作。
// * set 带有一个参数的函数，当属性被设置时调用。
// * enumerable 与数据属性的相同，是否可枚举（不定义默认为false）。
// * configurable 与属性的相同，是否可删除和可配置（不定义默认为false）。

// 例子：
let users = {
	name: 'John',
	surname: 'Smith',
}

Object.defineProperty(users, 'fullName', {
	get() {
		return `${this.name} ${this.surname}`
	},

	set(value) {
		;[this.name, this.surname] = value.split(' ')
	},

	// enumerable: true,
	// configurable: true
})

console.log(users.fullName) // John Smith
console.log(Object.keys(users)) // [ 'name', 'surname' ]

// * 注意：一个属性要么是 [访问器属性]（具有 get/set 方法），要么是 [数据属性]，但不能两者都是。

// 错误的例子：
// Object.defineProperty({}, 'prop', {
// 	value: 'Jake',
// 	get() {
// 		return 'John'
// 	},
// })

// **********************************************************************************************************************************

// * getter/setter 可以用作“真实”属性值的包装器，以便对它们进行更多的控制。

// 例子：禁止短的 user 的 name

let user2 = {
	get name() {
		return this._name
	},

	set name(value) {
		if (value.length < 4) {
			console.log('名字不能小于4个字符')
			return
		}

		this._name = value
	},
}

user2.name = 'Pete'
console.log(user2.name) // Pete
console.log(user2._name) // Pete（众所周知的约定，即 _ 以下划线开头的属性是内部属性，不应该从对象外部访问）

user2.name = 'WYN' // 名字不能小于4个字符
console.log(user2.name) // Pete

// **********************************************************************************************************************************

// 兼容性例子：更据生日计算出年龄
function User(name, birthday) {
	this.name = name
	this.birthday = birthday

	// 年龄是根据当前日期和生日计算出来的
	Object.defineProperty(this, 'age', {
		get() {
			return new Date().getFullYear() - this.birthday.getFullYear()
		},
	})
}

let john = new User('John', new Date(1996, 9, 26))

console.log(john.age) // 28

john.age = 108 // 没有 set 修改无效

console.log(john.age) // 28
console.log(john.birthday.getMonth() + '-' + john.birthday.getDate()) // 9-26
